// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package filepath

import (
	"errors"
	"io/fs"
	"os"
	"runtime"
	"syscall"
)

func walkSymlinks(path string) (string, error) {
	volLen := volumeNameLen(path)
	pathSeparator := string(os.PathSeparator)

	if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
		volLen++
	}
	vol := path[:volLen]
	dest := vol
	linksWalked := 0
	for start, end := volLen, volLen; start < len(path); start = end {
		for start < len(path) && os.IsPathSeparator(path[start]) {
			start++
		}
		end = start
		for end < len(path) && !os.IsPathSeparator(path[end]) {
			end++
		}

		// 在Windows上，“.”可以是符号链接。
		// 我们查找它，如果它是绝对值，则使用该值。
		// 如果没有，我们只返回“”。
		isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "."

		// 下一个路径组件位于路径[start:end]中。
		if end == start {
			// 不再有路径组件。
			break
		} else if path[start:end] == "." && !isWindowsDot {
			// 忽略路径组件“”。
			continue
		} else if path[start:end] == ".." {
			// 如果可能，请备份到以前的组件。
			// 请注意，volLen包含任何前导斜杠。

			// 将r设置为dest中最后一个斜杠的索引，
			// 位于卷之后。
			var r int
			for r = len(dest) - 1; r >= volLen; r-- {
				if os.IsPathSeparator(dest[r]) {
					break
				}
			}
			if r < volLen || dest[r+1:] == ".." {
				// 任何一条路径都没有斜杠
				// （它是空的或只有“C:”）
				// 或者它以“.”结尾。我们必须保留。
				// 无论哪种方式，请保留此“.”。
				if len(dest) > volLen {
					dest += pathSeparator
				}
				dest += ".."
			} else {
				// 放弃上次斜杠后的所有内容。
				dest = dest[:r]
			}
			continue
		}

		// 普通路径组件。将其添加到结果中。

		if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
			dest += pathSeparator
		}

		dest += path[start:end]

		// 解析符号链接。

		fi, err := os.Lstat(dest)
		if err != nil {
			return "", err
		}

		if fi.Mode()&fs.ModeSymlink == 0 {
			if !fi.Mode().IsDir() && end < len(path) {
				return "", syscall.ENOTDIR
			}
			continue
		}

		// 找到符号链接。在Windows上，如果“.”是相对符号链接，则返回“.”。

		linksWalked++
		if linksWalked > 255 {
			return "", errors.New("EvalSymlinks: too many links")
		}

		link, err := os.Readlink(dest)
		if err != nil {
			return "", err
		}

		if isWindowsDot && !IsAbs(link) {
			break
		}

		path = link + path[end:]

		v := volumeNameLen(link)
		if v > 0 {
			// 驱动器名的符号链接是一个绝对路径。
			if v < len(link) && os.IsPathSeparator(link[v]) {
				v++
			}
			vol = link[:v]
			dest = vol
			end = len(vol)
		} else if len(link) > 0 && os.IsPathSeparator(link[0]) {
			// 符号链接到绝对路径。
			dest = link[:1]
			end = 1
		} else {
			// 符号链接到相对路径；替换dest中最后一个
			// 路径组件。
			var r int
			for r = len(dest) - 1; r >= volLen; r-- {
				if os.IsPathSeparator(dest[r]) {
					break
				}
			}
			if r < volLen {
				dest = vol
			} else {
				dest = dest[:r]
			}
			end = 0
		}
	}
	return Clean(dest), nil
}
